home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung / Power-Programmierung (Tewi)(1994).iso / magazine / drdobbs / 1990 / 03 / williams.lst < prev    next >
File List  |  1990-02-13  |  40KB  |  1,235 lines

  1. HOMEGROWN DEBUGGING -- 386 STYLE!
  2. by Al Williams
  3.  
  4. [LISTING ONE]
  5.  
  6. ;******************************************************************************
  7. ;* File: BREAK386.ASM                                                         *
  8. ;* BREAK386 "main programs". Contains setup386, clear386, break386 and        *
  9. ;* int1_386.                                                                  *
  10. ;*     Williams - June, 1989                                                  *
  11. ;* Compile with: MASM /Ml BREAK386;                                           *
  12. ;******************************************************************************
  13. .MODEL small
  14. .386P
  15.  
  16.        public _break386,_clear386,_setup386,_int1_386
  17.  
  18. ; Set up stack offsets for word size arguments based on the code size
  19. ; Be careful, regardless of what Microsoft's documentation says,
  20. ; you must use @CodeSize (not @codesize, etc.) when compiling with /Ml
  21.  
  22. IF @CodeSize               ; True for models with far code
  23. arg1       EQU  <[BP+6]>
  24. arg2       EQU  <[BP+8]>
  25. arg3       EQU  <[BP+10]>
  26. arg4       EQU  <[BP+12]>
  27. ELSE
  28. arg1       EQU  <[BP+4]>
  29. arg2       EQU  <[BP+6]>
  30. arg3       EQU  <[BP+8]>
  31. arg4       EQU  <[BP+10]>
  32. ENDIF
  33.  
  34.  
  35.  
  36. .DATA
  37. ; Things you may want to change:
  38. DIRECT      EQU 0        ; IF 0 use BIOS; IF 1 use direct video access
  39. STKWRD      EQU 32       ; # of words to dump off the stack
  40. INTSTACK    EQU 1        ; When 0 don't display interrupt stack words
  41. USE_INT1    EQU 1        ; Set to 0 to disable int1_386()
  42.  
  43. oldoffset   dw 0         ; old interrupt 1 vector offset
  44. oldsegment  dw 0         ; old interrupt 1 vector segment
  45.  
  46. IF USE_INT1
  47. video       dw 0b000H    ; segment of video adapter (changed by vinit)
  48. csip        db 'CODE=',0
  49. done        db 'Program terminated normally.',0
  50. notdone     db 'Program breakpoint:',0
  51. stkmess     db 'Stack dump:',0
  52.  
  53. vpage       db 0
  54. vcols       db 80
  55.  
  56. IFE DIRECT
  57. prompt      db '<V>iew output, <T>race toggle, <C>ontinue or <A>bort? ',0
  58. savcursor   dw 0         ; inactive video cursor
  59. ALIGN 4
  60. vbuff       dd 1000 dup (07200720H)
  61. ELSE
  62. cursor      dw 0
  63. color       db 7
  64. ENDIF
  65. ENDIF
  66.  
  67. .CODE
  68.  
  69. ; This is the start up code. The old interrupt one vector is saved in
  70. ; oldsegment, oldoffset. int1_386 does not chain to the old vector, it
  71. ; simply replaces it.
  72.  
  73. _setup386 proc
  74.         push bp
  75.         mov bp,sp
  76.         push es
  77.         mov ax,3501H               ; get old int1 vector
  78.         int 21h
  79.         mov ax,es
  80.         mov oldsegment,ax
  81.         mov oldoffset,bx
  82.         pop es
  83.         mov ax,arg2                ; get new interrupt handler address
  84.         push ds
  85.         mov dx,arg1
  86. ; If int1_386 is being assembled, setup386 will check to see if you are
  87. ; installing int1386. If so, it will call vinit to set up the video parameters
  88. ; that int1_386 requires.
  89. IF USE_INT1
  90.         cmp ax,seg _int1_386
  91.         jnz notus
  92.         cmp dx,offset _int1_386
  93.         jnz notus
  94.         push dx
  95.         push ax
  96.         call vinit                 ; Int'l video if it is our handler
  97.         pop ds
  98.         pop dx
  99. ENDIF
  100. notus:  mov ax,2501H               ; Store interrupt address in vector table
  101.         int 21H
  102.         pop ds
  103.         xor eax,eax                ; Clear DR7/DR6 (just in case)
  104.         mov dr7,eax
  105.         mov dr6,eax
  106.         pop bp
  107.         ret
  108. _setup386 endp
  109.  
  110.  
  111. ; This routine sets/clears breakpoints
  112. ; Inputs:
  113. ;    breakpoint # (1-4)
  114. ;    breakpoint type (see BREAK386.INC)
  115. ;    segment/offset of break address (or null to clear breakpoint)
  116. ; Outputs:
  117. ;    AX=0 If successful
  118. ;    AX=-1 If not successful
  119.  
  120. _break386 proc
  121.         push bp
  122.         mov bp,sp
  123.         mov bx,arg1                     ; breakpoint # (1-4)
  124.         cmp bx,1
  125.         jb outrange
  126.         cmp bx,4
  127.         jna nothigh
  128. outrange:
  129.         mov ax,0ffffH                   ; error: breakpoint # out of range
  130.         pop bp
  131.         ret
  132. nothigh:
  133.         movzx eax,word ptr arg4         ; get breakpoint address
  134.         shl eax,4
  135.         movzx edx,word ptr arg3         ; calculate linear address
  136.         add eax,edx                     ; if address = 0 then
  137.         jz resetbp                      ; turn breakpoint off!
  138.         dec bx                          ; set correct address register
  139.         jz bp0
  140.         dec bx
  141.         jz bp1
  142.         dec bx
  143.         jz bp2
  144.         mov dr3,eax
  145.         jmp short brcont
  146. bp0:    mov dr0,eax
  147.         jmp short brcont
  148. bp1:    mov dr1,eax
  149.         jmp short brcont
  150. bp2:    mov dr2,eax
  151. brcont:
  152.         movzx eax,word ptr arg2         ; get type
  153.         mov cx,arg1                     ; calculate proper position
  154.         push cx
  155.         dec cx
  156.         shl cx,2
  157.         add cx,16
  158.         shl eax,cl                      ; rotate type
  159.         mov edx,0fh
  160.         shl edx,cl                      ; calculate type mask
  161.         not edx
  162.         pop cx
  163.         shl cx,1                        ; calculate position of enable bit
  164.         dec cx
  165.         mov ebx,1
  166.         shl ebx,cl
  167.         or eax,ebx                      ; enable bp
  168.         mov ebx,dr7                     ; get old DR7
  169.         and ebx,edx                     ; mask out old type
  170.         or ebx,eax                      ; set new type/enable bits
  171. ; Adjust enable bit (set on for data bp's, off if no data bp's)
  172. adjge:
  173.         mov eax,200H
  174.         and ebx,0fffffdffH              ; reset GE bit
  175.         test ebx,033330000H             ; test for data bp's
  176.         jz nodatabp
  177.         or ebx,512
  178. nodatabp:
  179.         mov dr7,ebx
  180.         pop bp
  181.         xor ax,ax
  182.         ret
  183. ; Here we reset a breakpoint by turning off it's enable bit & setting type to 0
  184. ; Clearing the type is required so that disabling all data breakpoints will
  185. ; clear the GE bit also.
  186. resetbp:
  187.         mov cx,bx                       ; calculate type/len bit positions
  188.         mov edx,0fh
  189.         dec cx
  190.         shl cx,2
  191.         add cx,16
  192.         shl edx,cl
  193.         not edx
  194.         mov cx,bx                       ; calculate enable bit position
  195.         shl cx,1
  196.         dec cx
  197.         mov eax,1
  198.         shl eax,cl
  199.         not ax                          ; flip bits
  200.         mov ebx,dr7
  201.         and ebx,eax                     ; clear enable
  202.         and ebx,edx                     ; clear type
  203.         jmp adjge
  204. _break386 endp
  205.  
  206.  
  207.  
  208. ; Reset the debug register, disabling all breakpoint. Also restore the old
  209. ; interrupt 1 vector
  210. _clear386 proc
  211.         pushf
  212.         pop ax
  213.         and ax,0FEFFH                   ; turn off trace flag
  214.         push ax
  215.         popf
  216.         xor eax,eax                     ; turn off all other breakpoints
  217.         mov dr7,eax
  218.         mov dr0,eax
  219.         mov dr1,eax
  220.         mov dr2,eax
  221.         mov dr3,eax
  222.         mov dr6,eax
  223.         mov ax,2501H                    ; restore old int 1 vector
  224.         push ds
  225.         mov bx,oldsegment
  226.         mov dx,oldoffset
  227.         mov ds,bx
  228.         int 21H
  229.         pop ds
  230.         ret
  231. _clear386 endp
  232.  
  233. IF USE_INT1
  234. ; This is all code relating to the optional INT 1 handler
  235.  
  236. ; This macro is used to get a register value off the stack and display it
  237. ; R is the register name and n is the position of the register on the stack
  238. ; i.e.: outreg 'AX',10
  239.  
  240. outreg   macro r,n
  241.          mov ax,&r
  242.          mov dx,[ebp+&n SHL 1]
  243.          call regout
  244.          endm
  245.  
  246.  
  247. ; This is the interrupt 1 handler
  248. _int1_386  proc far
  249.          sti                            ; Enable interrupts (see text)
  250.          pusha                          ; Save all Registers
  251.          push ds
  252.          push es
  253.          push ss
  254.          push @data
  255.          pop ds                         ; Reload DS
  256.          mov bp,sp                      ; point ebp to top of stack
  257. IFE DIRECT
  258.         call savevideo
  259. ENDIF
  260.          mov ax,video                   ; get video addressabilty
  261.          mov es,ax
  262.          assume cs:@code,ds:@data
  263.          mov bx,offset notdone          ; Display breakpoint message
  264.          call outstr
  265.          mov edx,dr6
  266.          call  hexout
  267.          xor edx,edx
  268.          mov dr6,edx
  269.          call  crlf
  270. ;do register dump
  271.          outreg 'AX',10
  272.          outreg 'FL',13
  273.          outreg 'BX',7
  274.          outreg 'CX',9
  275.          outreg 'DX',8
  276.          call  crlf
  277.          outreg 'SI',4
  278.          outreg 'DI',3
  279.          outreg 'SP',6
  280.          outreg 'BP',5
  281.          call  crlf
  282.          outreg 'CS',12
  283.          outreg 'IP',11
  284.          outreg 'DS',2
  285.          outreg 'ES',1
  286.          outreg 'SS',0
  287.          call  crlf
  288.  ; do stack dump
  289. IF STKWRD
  290.          mov bx,offset stkmess
  291.          call outstr                    ; Print stack dump title
  292.          push fs
  293.          mov dx,[ebp]                   ; get program's ss
  294.          mov fs,dx
  295.          mov al,'('
  296.          call  ouch
  297.          mov al,' '
  298.          call  ouch
  299.          call  hexout
  300.          mov al,':'
  301.          call  ouch
  302.          mov al,' '
  303.          call  ouch
  304.          mov bx,[ebp+12]                ; get stack pointer (before pusha)
  305. IFE INTSTACK
  306.          add bx,6                       ; skip interrupt info if desired
  307. ENDIF
  308.          mov dx,bx
  309.          push bx
  310.          call  hexout
  311.          mov al,')'
  312.          call  ouch
  313.          call  crlf
  314.          pop bx
  315.          mov cx,STKWRD
  316. sloop:
  317.  
  318.          mov dx,fs:[bx]                 ; get word at stack
  319.          push bx
  320.          push cx
  321.          call  hexout                    ; display it
  322.          pop cx
  323.          pop bx
  324.          inc bx
  325.          inc bx
  326.          loop sloop
  327.          pop fs
  328. ENDIF
  329. nostack:
  330. ; Here we will dump 16 bytes starting 8 bytes prior to the instruction
  331. ; that caused the break
  332.          push fs
  333.          call  crlf
  334.          mov bx, offset csip
  335.          call outstr
  336.          mov cx,8
  337.          mov ax,[ebp+24]                ; get cs
  338.          mov fs,ax
  339.          mov bx,[ebp+22]                ; get ip
  340.          cmp bx,8                       ; make sure we have 8 bytes before
  341.          jnb ipbegin                    ; the begining of the segment
  342.          mov cx,bx                      ; If not, only dump from the start
  343. ipbegin: sub bx,cx                      ; of the segment
  344.          push bx
  345.          push cx
  346.          mov dx,ax                      ; display address
  347.          call  hexout
  348.          mov al,':'
  349.          call  ouch
  350.          mov al,' '
  351.          call  ouch
  352.          mov dx,bx
  353.          call  hexout
  354.          mov al,'='
  355.          call  ouch
  356.          pop cx
  357.          pop bx
  358.          or bx,bx                       ; if starting at 0, don't display any
  359.          jz ipskip                      ; before IP
  360. iploop:
  361.          mov dl,fs:[bx]                 ; get byte
  362.          push bx
  363.          push cx
  364.          call  hex1out                   ; output it
  365.          pop cx
  366.          pop bx
  367.          inc bx
  368.          loop iploop
  369. ipskip:
  370.          push bx
  371.          mov al,'*'                     ; put '*' before IP location
  372.          call  ouch
  373.          mov al,' '
  374.          call  ouch
  375.          pop bx
  376. ; This is basically a repeat of the above loop except it dumps the 8 bytes
  377. ; starting at IP
  378.          mov cx,8
  379. xiploop:
  380.          mov dl,fs:[bx]
  381.          push bx
  382.          push cx
  383.          call  hex1out
  384.          pop cx
  385.          pop bx
  386.          inc bx
  387.          loop xiploop
  388.          call  crlf
  389.          call  crlf
  390.          pop fs
  391. IFE DIRECT
  392. ; Here we will ask if we should continue or abort
  393.          mov bx,offset prompt
  394.          call outstr
  395. keyloop:
  396.          xor ah,ah                      ; Get keyboard input
  397.          int 16H
  398.          and al,0dfh                    ; make upper case
  399.          cmp al,'T'
  400.          jz ttoggle
  401.          cmp al,'A'
  402.          jz q1
  403.          cmp al,'C'
  404.          jz c1
  405.          cmp al,'V'
  406.          jnz keyloop
  407. ; Display program's screen until any key is pressed
  408.          call savevideo
  409.          xor ah,ah
  410.          int 16H
  411.          call savevideo
  412.          jmp keyloop
  413.  
  414. ; Execution comes here to toggle trace flag and continue
  415. ttoggle:
  416.          xor word ptr [bp+26],256       ; toggle trace flag on stack
  417.  
  418. ; Execution comes here to continue running the target program
  419. c1:
  420.          call  crlf
  421. IFE DIRECT
  422.          call savevideo
  423. ELSE
  424.          xor ax,ax
  425.          mov cursor,ax
  426. ENDIF
  427.          pop ss
  428.          pop es
  429.          pop ds
  430.          popa
  431. ; This seems complicated at first.
  432. ; You MUST insure that RF is set before continuing. If RF is not set
  433. ; you will just cause a breakpoint immediately!
  434. ; In protected mode, this is handled automatically. In real mode it
  435. ; isn't since RF is in the high 16 bits of the flags register.
  436. ; Essentially we have to convert the stack from:
  437. ;
  438. ;       16 bit Flags                 32 bit flags (top word = 1 to set RF)
  439. ;       16 bit CS       to ----->    32 bit CS    (garbage in top 16 bits)
  440. ;       16 bit IP                    32 bit IP    (top word = 0)
  441. ;
  442. ; All this so we can execute an IRETD which will change RF.
  443.  
  444.          sub esp,6            ; make a double stack frame
  445.          xchg ax,[esp+6]      ; get ip in ax
  446.          mov [esp],ax         ; store it
  447.          xor ax,ax
  448.          mov [esp+2],ax       ; eip = 0000:ip
  449.          mov ax,[esp+6]
  450.          xchg ax,[esp+8]      ; get cs
  451.          mov [esp+4],ax
  452.          xor ax,ax
  453.          mov [esp+6],ax
  454.          mov ax,[esp+8]       ; zero that stack word & restore ax
  455.          xchg ax,[esp+10]     ; get flags
  456.          mov [esp+8],ax
  457.          mov ax,1             ; set RF
  458.          xchg ax,[esp+10]
  459.          iretd                ; DOUBLE IRET (32 bits!)
  460.  
  461. ENDIF
  462.  
  463. ; Execution resumes here to abort the target program
  464. q1:
  465. IFE DIRECT
  466.          call savevideo
  467. ENDIF
  468.          call quit
  469. _int1_386  endp
  470.  
  471. IFE DIRECT
  472. ; save video screen & restore ours (only with BIOS please!)
  473. ; (assumes 25 lines/page)
  474. savevideo proc near
  475.         pusha
  476.         push es
  477.         mov ah,0fh
  478.         int 10h                         ; reread video page/size in case
  479.         mov vpage,bh                    ; program changed it
  480.         mov vcols,ah
  481.  
  482.         push savcursor
  483.         mov ah,3                        ; get old cursor
  484.         mov bh,vpage
  485.         int 10H
  486.         mov savcursor,dx
  487.         pop dx
  488.         mov ah,2                        ; set new cursor
  489.         int 10H
  490.         movzx ax,vpage
  491.         mov cl,vcols                    ; compute # bytes/page
  492.         xor ch,ch
  493.         mov dx,cx                       ; vcols * 25 * 2
  494.         shl cx,3
  495.         shl dx,1
  496.         add cx,dx
  497.         mov dx,cx
  498.         shl cx,2
  499.         add cx,dx
  500.         push cx
  501.         mul cx
  502.         mov di,ax                       ; start at beginning of page
  503.         pop cx
  504.         shr cx,2                        ; # of double words to transfer
  505.         mov ax,video
  506.         mov es,ax
  507.         mov si,offset vbuff             ; store inactive screen in vbuff
  508. xloop:  mov eax,es:[di]                 ; swap screens
  509.         xchg eax,[si]
  510.         mov es:[di],eax
  511.         add si,4
  512.         add di,4
  513.         loop xloop
  514.         pop es
  515.         popa
  516.         ret
  517. savevideo endp
  518. ENDIF
  519.  
  520.  
  521. ; This routine prints a register value complete with label
  522. ; The register name is in AX and the value is in dx (see the outreg macro)
  523. regout  proc near
  524.         push dx
  525.         push ax
  526.         mov al,ah
  527.         call  ouch
  528.         pop ax
  529.         call  ouch
  530.         mov al,'='
  531.         call  ouch
  532.         pop dx
  533.         call  hexout
  534.         ret
  535. regout  endp
  536.  
  537. ; Plain vanilla hexadecimal digit output routine
  538. hexdout proc near
  539.         and dl,0fh
  540.         add dl,'0'
  541.         cmp dl,3ah
  542.         jb ddigit
  543.         add dl,'A'-3ah
  544. ddigit:
  545.         mov al,dl
  546.         call ouch
  547.         ret
  548. hexdout endp
  549.  
  550. ; Plain vanilla hexadecimal word output routine
  551. hexout  proc near
  552.         push dx
  553.         shr dx,12
  554.         call hexdout
  555.         pop dx
  556.         push dx
  557.         shr dx,8
  558.         call hexdout
  559.         pop dx
  560. ; Call with this entry point to output just a byte
  561. hex1out:
  562.         push dx
  563.         shr dx,4
  564.         call hexdout
  565.         pop dx
  566.         call hexdout
  567.         mov al,' '
  568.         call ouch
  569.         ret
  570. hexout  endp
  571.  
  572.  
  573. ; These routines are for direct video output. Using them allows you to
  574. ; debug video bios calls, but prevents you from single stepping
  575. IF DIRECT
  576. ;output a character in al assumes ds=dat es=video destroys bx,ah
  577. ouch     proc near
  578.          mov bx,cursor
  579.          mov ah,color
  580.          mov es:[bx],ax
  581.          inc bx
  582.          inc bx
  583.          mov cursor,bx
  584.          ret
  585. ouch     endp
  586.  
  587. ; <CR> <LF> output.  assumes ds=dat es=video  destroys ax,cx,dx,di  clears df
  588. crlf     proc near
  589.          mov ax,cursor
  590.          mov cx,160
  591.          xor dx,dx
  592.          div cx
  593.          inc ax
  594.          mul cx
  595.          mov cursor,ax
  596.          mov cx,80
  597.          mov ah,color
  598.          mov al,' '
  599.          mov di,cursor
  600.          cld
  601.          rep stosw
  602.          ret
  603. crlf     endp
  604.  
  605. ELSE
  606. ; These are the BIOS output routines
  607. ; Output a character
  608. ouch     proc near
  609.          mov ah,0eh
  610.          mov bh,vpage
  611.          int 10h
  612.          ret
  613. ouch     endp
  614.  
  615. ; <CR> <LF> output.
  616. crlf     proc near
  617.          mov al,0dh
  618.          call ouch
  619.          mov al,0ah
  620.          call ouch
  621.          ret
  622. crlf     endp
  623.  
  624.  
  625.  
  626. ENDIF
  627.  
  628. ; Intialize the video routines
  629. vinit    proc near
  630.          mov ah,0fh
  631.          int 10h
  632.          mov vcols,ah
  633.          mov vpage,bh
  634.          cmp al,7                       ; monochrome
  635.          mov ax,0b000H
  636.          jz vexit
  637.          mov ax,0b800H
  638. vexit:   mov video,ax
  639.          ret
  640. vinit    endp
  641.  
  642.  
  643. ; outputs string pointed to by ds:bx (ds must be dat) es= video when DIRECT=1
  644. outstr   proc near
  645. outagn:
  646.          mov al,[bx]
  647.          or al,al
  648.          jz outout
  649.          push bx
  650.          call ouch
  651.          pop bx
  652.          inc bx
  653.          jmp outagn
  654. outout:  ret
  655. outstr   endp
  656.  
  657.  
  658. ; This routine is called to return to DOS
  659. quit     proc near
  660.          call _clear386
  661.          mov ax,4c00h                   ; Return to DOS
  662.          int 21h
  663. quit     endp
  664.  
  665.  
  666. ENDIF
  667.  
  668.         end
  669.  
  670.  
  671. [LISTING TWO
  672.  
  673.  
  674. ;******************************************************************************
  675. ;* File: BREAK386.INC                                                         *
  676. ;* Header file to include with assembly language programs using BREAK386      *
  677. ;*     Williams - June, 1989                                                  *
  678. ;******************************************************************************
  679.  
  680. IF @CodeSize       ; If large style models
  681.         extrn _break386:far,_clear386:far,_setup386:far,_int1_386:far
  682. ELSE
  683.         extrn _break386:near,_clear386:near,_setup386:near,_int1_386:far
  684. ENDIF
  685.  
  686. ; Breakpoint equates
  687. BP_CODE     EQU  0                 ; CODE BREAKPOINT
  688. BP_DATAW1   EQU  1                 ; ONE BYTE DATA WRITE BREAKPOINT
  689. BP_DATARW1  EQU  3                 ; ONE BYTE DATA R/W BREAKPOINT
  690. BP_DATAW2   EQU  5                 ; TWO BYTE DATA WRITE BREAKPOINT
  691. BP_DATARW2  EQU  7                 ; TWO BYTE DATA R/W BREAKPOINT
  692. BP_DATAW4   EQU 13                 ; FOUR BYTE DATA WRITE BREAKPOINT
  693. BP_DATARW4  EQU 15                 ; FOUR BYTE DATA R/W BREAKPOINT
  694.  
  695. ; Macros to turn tracing on and off
  696. ; Note: When tracing, you will actually "see" traceoff before it turns
  697. ;       tracing off
  698.  
  699. traceon  macro
  700.          push bp
  701.          pushf
  702.          mov bp,sp
  703.          xchg ax,[bp]
  704.          or ax,100H
  705.          xchg ax,[bp]
  706.          popf
  707.          pop bp
  708.          endm
  709.  
  710. traceoff macro
  711.          push bp
  712.          pushf
  713.          mov bp,sp
  714.          xchg ax,[bp]
  715.          and ax,0FEFFH
  716.          xchg ax,[bp]
  717.          popf
  718.          pop bp
  719.          endm
  720.  
  721.  
  722. [LISTING THREE]
  723.  
  724. /******************************************************************************
  725.  * File: BREAK386.H                                                           *
  726.  * C Header for C programs using BREAK386 or CBRK386                          *
  727.  *     Williams - June, 1989                                                  *
  728.  *****************************************************************************/
  729.  
  730. #ifndef NO_EXT_KEYS
  731.   #define _CDECL cdecl
  732. #else
  733.   #define _CDECL
  734. #endif
  735.  
  736. #ifndef BR386_HEADER
  737.   #define BR386_HEADER
  738.  
  739.   /* declare functions */
  740.   void  _CDECL setup386(void (_CDECL interrupt far *)());
  741.   void  _CDECL csetup386(void (_CDECL far *)());
  742.   void  _CDECL clear386(void);
  743.   int   _CDECL break386(int,int, void far *);
  744.   void  _CDECL far interrupt int1_386();
  745.  
  746.   /* breakpoint types */
  747.   #define BP_CODE        0                 /* CODE BREAKPOINT*/
  748.   #define BP_DATAW1      1                 /* ONE BYTE DATA WRITE BREAKPOINT*/
  749.   #define BP_DATARW1     3                 /* ONE BYTE DATA R/W BREAKPOINT*/
  750.   #define BP_DATAW2      5                 /* TWO BYTE DATA WRITE BREAKPOINT*/
  751.   #define BP_DATARW2     7                 /* TWO BYTE DATA R/W BREAKPOINT*/
  752.   #define BP_DATAW4     13                 /* FOUR BYTE DATA WRITE BREAKPOINT*/
  753.   #define BP_DATARW4    15                 /* FOUR BYTE DATA R/W BREAKPOINT*/
  754.  
  755. #endif
  756.  
  757.  
  758. [LISTING FOUR]
  759.  
  760. ;******************************************************************************
  761. ;* File: DEBUG386.ASM                                                         *
  762. ;* Example assembly language program for use with BREAK386                    *
  763. ;*     Williams - June, 1989                                                  *
  764. ;* Compile with: MASM /Ml DEBUG386.ASM;                                       *
  765. ;******************************************************************************
  766.  
  767. .model large
  768. .386
  769. INCLUDE break386.inc
  770. .stack 0a00H
  771.  
  772. .data
  773. align 2                                  ; make sure this is word aligned
  774. memcell dw 0                             ; cell to write to
  775.  
  776.  
  777. .code
  778.  
  779. main    proc
  780. ;setup data segment
  781.         mov ax,@data
  782.         mov ds,ax
  783.         assume cs:@code,ds:@data
  784.  
  785. ; start debugging
  786.         push seg _int1_386               ; segment of interrupt handler
  787.         push offset _int1_386            ; offset of interrupt handler
  788.         call _setup386
  789.         add sp,4                         ; balance stack (like a call to C)
  790. ; set up a starting breakpoint
  791.         push seg bp1                     ; segment of breakpoint
  792.         push offset bp1                  ; offset of breakpoint
  793.         push BP_CODE                     ; breakpoint type
  794.         push 1                           ; breakpoint # (1-4)
  795.         call _break386
  796.         add sp,8                         ; balance the stack
  797.  
  798.         push seg bp2                     ; set up breakpoint #2
  799.         push offset bp2
  800.         push BP_CODE
  801.         push 2
  802.         call _break386
  803.         add sp,8
  804.  
  805.         push seg bp3                    ; set up breakpoint #3
  806.         push offset bp3
  807.         push BP_CODE
  808.         push 3
  809.         call _break386
  810.         add sp,8
  811.  
  812.         push @data                      ; set up breakpoint #4 (data)
  813.         push offset memcell
  814.         push BP_DATAW2
  815.         push 4
  816.         call _break386
  817.         add sp,8
  818.  
  819.  
  820.  
  821. bp1:
  822.         mov cx,20                       ; loop 20 times
  823. loop1:
  824.         mov dl,cl                       ; print some letters
  825.         add dl,'@'
  826.         mov ah,2
  827. bp2:
  828.         int 21h
  829. bp3:
  830.         loop loop1                      ; repeat
  831.  
  832.         mov bx,offset memcell           ; point bx at memory cell
  833.         mov ax,[bx]                     ; read cell (no breakpoint)
  834.         mov [bx],ah                     ; this should cause breakpoint 4
  835.         call _clear386                  ; shut off debugging
  836.         mov ah,4ch
  837.         int 21h                         ; back to DOS
  838. main    endp
  839.         end main
  840.  
  841.  
  842. [LISTING FIVE]
  843.  
  844. /****************************************************************************
  845.  * File: DBG386.C                                                           *
  846.  * Example C program using BREAK386 with the built in interrupt handler     *
  847.  * Al Williams  -- 15 July 1989                                             *
  848.  * Compile with: CL DBG386.C BREAK386                                       *
  849.  ****************************************************************************/
  850. #include <stdio.h>
  851. #include <dos.h>
  852. #include "break386.h"
  853.  
  854. int here[10];
  855. void far *bp;
  856. int i;
  857.  
  858. main()
  859. {
  860.   int j;
  861.   setup386(int1_386);          /* set up debugging */
  862.   bp=(void far *)&here[2];     /* make long pointer to data word */
  863.   break386(1,BP_DATAW2,bp);    /* set breakpoint */
  864.   for (j=0;j<2;j++) {          /* loop twice */
  865.     for (i=0;i<10;i++)         /* for each element in here[] */
  866.      {
  867.      char x;
  868.      putchar(i+'0');           /* print index digit */
  869.      here[i]=i;                /* assign # to array element */
  870.      }
  871.   break386(1,0,NULL);          /* turn off breakpoint on 2nd pass */
  872.   }
  873.   clear386();                  /* turn off debugging */
  874. }
  875.  
  876.  
  877. [LISTING SIX]
  878.  
  879. ;******************************************************************************
  880. ;* File: DBGOFF.ASM                                                           *
  881. ;* Try this program if you leave a program abnormally (say, with a stack      *
  882. ;* overflow). It will reset the debug register.                               *
  883. ;*     Williams - June, 1989                                                  *
  884. ;* Compile with: MASM DBGOFF;                                                 *
  885. ;******************************************************************************
  886.  
  887. .model small
  888. .386P
  889. .stack 32
  890. .code
  891.  
  892. main proc
  893.      xor eax,eax                        ; clear dr7
  894.      mov dr7,eax
  895.      mov ah,4ch                         ; exit to DOS
  896.      int 21H
  897. main endp
  898.      end main
  899.  
  900.  
  901. [LISTING SEVEN]
  902.  
  903. ;******************************************************************************
  904. ;* File: CBRK386.ASM                                                          *
  905. ;* Functions to allow breakpoint handlers to be written in C.                 *
  906. ;*     Williams - June, 1989                                                  *
  907. ;* Compile with: MASM /Ml CBRK386.ASM;                                        *
  908. ;******************************************************************************
  909. .MODEL small
  910. .386P
  911.  
  912.        public _csetup386
  913.  
  914. ; Set up stack offsets for word size arguments based on the code size
  915. ; Be careful, regardless of what Microsoft's documentation says,
  916. ; you must use @CodeSize (not @codesize, etc.)
  917.  
  918. IF @CodeSize               ; True for models with far code
  919. arg1       EQU  <[BP+6]>
  920. arg2       EQU  <[BP+8]>
  921. arg3       EQU  <[BP+10]>
  922. arg4       EQU  <[BP+12]>
  923. ELSE
  924. arg1       EQU  <[BP+4]>
  925. arg2       EQU  <[BP+6]>
  926. arg3       EQU  <[BP+8]>
  927. arg4       EQU  <[BP+10]>
  928. ENDIF
  929.  
  930.  
  931.  
  932. .DATA
  933. ; You may need to change the next line to expand the stack your breakpoint
  934. ; handler runs with
  935. STACKSIZE   EQU 2048
  936.  
  937. oldoffset   dw 0                        ; old interrupt 1 vector offset
  938. oldsegment  dw 0                        ; old interrupt 1 vector segment
  939. oldstack    equ this dword
  940. sp_save     dw 0
  941. ss_save     dw 0
  942. ds_save     dw 0
  943. es_save     dw 0
  944. ccall       equ this dword              ; C routine's adress is saved here
  945. c_off       dw 0
  946. c_seg       dw 0
  947. oldstkhqq   dw 0                        ; Old start of stack
  948.  
  949.  
  950. newsp       equ this dword              ; New stack address for C routine
  951.             dw offset stacktop
  952.             dw seg newstack
  953.  
  954. ; Here is the new stack. DO NOT MOVE IT OUT OF DGROUP
  955. ; That is, leave it in the DATA or DATA? segment.
  956. newstack    db STACKSIZE DUP (0)
  957. stacktop    EQU $
  958.             extrn STKHQQ:word           ; Microsoft heap/stack bound
  959.  
  960. .CODE
  961.  
  962.  
  963. ; This routine is called in place of setup386(). You pass it the address of
  964. ; a void far function that you want invoked on a breakpoint.
  965. ; It's operation is identical to setup386() except for:
  966. ;
  967. ;      1) The interrupt 1 vector is set to cint1_386() (see below)
  968. ;      2) The address passed is stored in location CCALL
  969. ;      3) DS and ES are stored in ds_save and es_save
  970.  
  971. _csetup386 proc
  972.         push bp
  973.         mov bp,sp
  974.         push es
  975.         mov ax,es
  976.         mov es_save,ax
  977.         mov ax,ds
  978.         mov ds_save,ax
  979.         mov ax,3501H
  980.         int 21h
  981.         mov ax,es
  982.         mov oldsegment,ax
  983.         mov oldoffset,bx
  984.         pop es
  985.         mov ax,arg2
  986.         push ds
  987.         mov dx,arg1
  988.         mov c_seg,ax
  989.         mov c_off,dx
  990.         mov ax,seg _cint1_386
  991.         mov ds,ax
  992.         mov dx,offset _cint1_386
  993.         mov ax,2501H
  994.         int 21H
  995.         pop ds
  996.         xor eax,eax
  997.         mov dr6,eax
  998.         pop bp
  999.         ret
  1000. _csetup386 endp
  1001.  
  1002. ;*****************************************************************************
  1003. ;*                                                                           *
  1004. ;* Here is the interrupt handler!!!                                          *
  1005. ;* Two arguments are passed to C, a far pointer to the base of the stack     *
  1006. ;* frame and the complete contents of dr6 as a long unsigned int.            *
  1007. ;*                                                                           *
  1008. ;* The stack frame is as follows:                                            *
  1009. ;*                                                                           *
  1010. ;*    .                                                                      *
  1011. ;*    .                                                                      *
  1012. ;*   (Interrupted code's stack)                                              *
  1013. ;*   FLAGS                                                                   *
  1014. ;*   CS                                                                      *
  1015. ;*   IP <DDDD?                                                               *
  1016. ;*   AX      3                                                               *
  1017. ;*   CX      3                                                               *
  1018. ;*   DX      3                                                               *
  1019. ;*   BX      3                                                               *
  1020. ;*   SP DDDDDY (Stack pointer points to IP above)                            *
  1021. ;*   BP                                                                      *
  1022. ;*   SI                                                                      *
  1023. ;*   DI                                                                      *
  1024. ;*   ES                                                                      *
  1025. ;*   DS                                                                      *
  1026. ;*   SS <DDDDD pointer passed to your routine points here                    *
  1027. ;*                                                                           *
  1028. ;* The pointer is two way. That is, you can read the values or set any of    *
  1029. ;* them except SS. You should, however, refrain from changing CS,IP,or SP.   *
  1030. ;*                                                                           *
  1031. ;*****************************************************************************/
  1032.  
  1033. _cint1_386 proc
  1034.         pusha                           ; save registers
  1035.         push es
  1036.         push ds
  1037.         push ss
  1038.         mov ax,@data                    ; point at our data segment
  1039.         mov ds,ax
  1040.         mov ax,ss
  1041.         mov ss_save,ax                  ; remember old stack location
  1042.         mov sp_save,sp
  1043.         cld
  1044.         lss sp,newsp                    ; switch stacks
  1045.         mov ax,STKHQQ                   ; save old end of stack
  1046.         mov oldstkhqq,ax
  1047.         mov ax,offset newstack          ; load new end of stack
  1048.         mov STKHQQ,ax
  1049.         sti
  1050.         mov eax,dr6                     ; put DR6 on stack for C
  1051.         push eax
  1052.         push ss_save                    ; put far pointer to stack frame
  1053.         push sp_save                    ; on new stack for C
  1054.         mov ax,es_save                  ; restore es/ds from csetup386()
  1055.         mov es,ax
  1056.         mov ax,ds_save
  1057.         mov ds,ax
  1058.         call ccall                      ; call the C program
  1059.         xor eax,eax                     ; clear DR6
  1060.         mov dr6,eax
  1061.         mov ax,@data
  1062.         mov ds,ax                       ; regain access to data
  1063.         lss sp,oldstack                 ; restore old stack
  1064.         add sp,2                        ; don't pop off SS
  1065.                                         ; (in case user changed it)
  1066.         mov ax,oldstkhqq                ; restore end of stack
  1067.         mov STKHQQ,ax
  1068.         pop ds
  1069.         pop es
  1070.         popa
  1071.  
  1072. ; This seems complicated at first.
  1073. ; You MUST insure that RF is set before continuing. If RF is not set
  1074. ; you will just cause a breakpoint immediately!
  1075. ; In protected mode, this is handled automatically. In real mode it
  1076. ; isn't since RF is in the high 16 bits of the flags register.
  1077. ; Essentially we have to convert the stack from:
  1078. ;
  1079. ;       16 bit Flags                 32 bit flags (top word = 1 to set RF)
  1080. ;       16 bit CS       to ----->    32 bit CS    (garbage in top 16 bits)
  1081. ;       16 bit IP                    32 bit IP    (top word = 0)
  1082. ;
  1083. ; All this so we can execute an IRETD which will change RF.
  1084.  
  1085.         sub esp,6                       ; make a double stack frame
  1086.         xchg ax,[esp+6]                 ; get ip in ax
  1087.         mov [esp],ax                    ; store it
  1088.         xor ax,ax
  1089.         mov [esp+2],ax                  ; eip = 0000:ip
  1090.         mov ax,[esp+6]
  1091.         xchg ax,[esp+8]                 ; get cs
  1092.         mov [esp+4],ax
  1093.         xor ax,ax
  1094.         mov [esp+6],ax
  1095.         mov ax,[esp+8]                  ; zero that stack word & restore ax
  1096.         xchg ax,[esp+10]                ; get flags
  1097.         mov [esp+8],ax
  1098.         mov ax,1                        ; set RF
  1099.         xchg ax,[esp+10]
  1100.         iretd                           ; DOUBLE IRET (32 bits!)
  1101. _cint1_386 endp
  1102.         end
  1103.  
  1104.  
  1105. [LISTING EIGHT]
  1106.  
  1107. /******************************************************************************
  1108.  * File: CBRKDEMO.C                                                           *
  1109.  * Example C interrupt handler for use with CBRK386                           *
  1110.  *     Williams - June, 1989                                                  *
  1111.  * Compile with: CL CBRKDEMO.C BREAK386 CBRK386                               *
  1112.  ******************************************************************************/
  1113.  
  1114.  
  1115. #include <stdio.h>
  1116. #include <conio.h>
  1117. #include <ctype.h>
  1118. #include <dos.h>
  1119. #include "break386.h"
  1120.  
  1121. /* functions we will reference */
  1122. int loop();
  1123. void far broke();
  1124.  
  1125.  
  1126. main()
  1127.   {
  1128.   int i;
  1129. /* declare function broke as our interrupt handler */
  1130.   csetup386(broke);
  1131.   break386(1,BP_CODE,(void far *)loop); /* set break at function loop */
  1132.  
  1133.   for (i=0;i<10;i++) loop(i);
  1134.   printf("Returned to main.\n");
  1135.  
  1136.   clear386();                           /* turn off debugging */
  1137.   }
  1138.  
  1139.  
  1140. /* This function has a breakpoint on its entry */
  1141. loop(int j)
  1142.   {
  1143.   printf("Now in loop (%d)\n",j);
  1144.   }
  1145.  
  1146. /*****************************************************************************
  1147.  *                                                                           *
  1148.  * Here is the interrupt handler!!!                                          *
  1149.  * Note it must be a far function (normal int the LARGE, HUGE & MEDIUM       *
  1150.  * models). Two arguments are passed: a far pointer to the base of the stack *
  1151.  * frame and the complete contents of dr6 as a long unsigned int.            *
  1152.  *                                                                           *
  1153.  * The stack frame is as follows:                                            *
  1154.  *                                                                           *
  1155.  *    .                                                                      *
  1156.  *    .                                                                      *
  1157.  *   (Interrupted code's stack)                                              *
  1158.  *   FLAGS                                                                   *
  1159.  *   CS                                                                      *
  1160.  *   IP <DDDD?                                                               *
  1161.  *   AX      3                                                               *
  1162.  *   CX      3                                                               *
  1163.  *   DX      3                                                               *
  1164.  *   BX      3                                                               *
  1165.  *   SP DDDDDY (Stack pointer points to IP above)                            *
  1166.  *   BP                                                                      *
  1167.  *   SI                                                                      *
  1168.  *   DI                                                                      *
  1169.  *   ES                                                                      *
  1170.  *   DS                                                                      *
  1171.  *   SS <DDDDD pointer passed to your routine points here                    *
  1172.  *                                                                           *
  1173.  * The pointer is two way. That is, you can read the values or set any of    *
  1174.  * them except SS. You should, however, refrain from changing CS,IP,or SP.   *
  1175.  *                                                                           *
  1176.  *****************************************************************************/
  1177.  
  1178. void far broke(void far *p,long dr6)
  1179.   {
  1180.   static int breaking=1;  /* don't do anything if breaking=0 */
  1181.   int c;
  1182.   if (breaking)
  1183.     {
  1184.     int n;
  1185.     int far *ip;
  1186. /*****************************************************************************
  1187.  *                                                                           *
  1188.  * Here we will read the local variable off the interrupted program's stack! *
  1189.  * Assuming small model, the stack above our stack frame looks like this:    *
  1190.  *      i   -  variable sent to loop                                         *
  1191.  *      add -  address to return to main with                                *
  1192.  *    <our stack frame starts here>                                          *
  1193.  *                                                                           *
  1194.  * This makes i the 15th word on the stack (16th on models with far code)    *
  1195.  *                                                                           *
  1196.  *****************************************************************************/
  1197.  
  1198. #define IOFFSET 15    /* use 16 for large, medium or huge models */
  1199.     n=*((unsigned int far *)p+IOFFSET);
  1200.     printf("\nBreakpoint reached! (DR6=%lX i=%d)\n",dr6,n);
  1201. /* Ask user what to do. */
  1202.     do {
  1203.        printf("<C>ontinue, <M>odify i, <A>bort, or <N>o breakpoint? ");
  1204.        c=getche();
  1205.        putch('\r');
  1206.        putch('\n');                     /* start a new line */
  1207.        if (!c)                          /* function key pressed */
  1208.           {
  1209.           getch();
  1210.           continue;
  1211.           }
  1212.        c=toupper(c);
  1213. /* Modify loop's copy of i (doesn't change main's i) */
  1214.        if (c=='M')
  1215.           {
  1216.           int newi;
  1217.           printf("Enter new value for i: ");
  1218.           scanf("%d",&newi);
  1219.           *((unsigned int far *)p+IOFFSET)=newi;
  1220.           continue;
  1221.           }
  1222.        if (c=='A')                      /* Exiting */
  1223.          {
  1224.          clear386();                    /* ALWAYS turn off debugging!!! */
  1225.          exit(0);
  1226.          }
  1227.        if (c=='N')
  1228.          breaking=0;  /* We could have turned off breakpoints instead */
  1229.        } while (c!='A'&&c!='N'&&c!='C');
  1230.     }
  1231.   }
  1232.  
  1233.  
  1234.  
  1235.